
#include "config.h"
#include "debug.h"
#include "app_config.h"

#include "STC32G_Timer.h"
#include "STC32G_GPIO.h"
#include "STC32G_NVIC.h"
#include "STC32G_Exti.h"
#include "STC32G_I2C.h"
#include "STC32G_Delay.h"
#include "STC32G_Switch.h"

#include "drv_ina219.h"

#if TCFG_DRV_INA219_SUPPORT

struct ina219_value_t ina219_value;

u16 ina219_calValue = 0;

u8  ina219_busVolt_LSB_mV = 4;      // Bus Voltage LSB value = 4mV
u8  ina219_shuntVolt_LSB_uV = 10;   // Shunt Voltage LSB value = 10uV

u32 ina219_current_LSB_uA;
u32 ina219_power_LSB_mW;
 
/**
 * @brief   INA219写寄存器
*/
void ina219_Write_Register(u8 reg, u16 dat)
{
  u8 val[2];
  
  val[0] = (u8)(dat >> 8);
  val[1] = (u8)(dat & 0xFF);
  I2C_WriteNbyte(INA219_I2C_ADDRESS, reg, val, 2);
}
 
/**
 * @brief   INA219读寄存器
*/
void ina219_Read_Register(u8 reg, u16 *dat)
{
  u8 val[2];

  I2C_ReadNbyte(INA219_I2C_ADDRESS, reg, val, 2);
  *dat = ((u16)(val[0]) << 8) + val[1]; 
}

// INA219 Set Calibration 16V/16A(Max) 0.02|?
void ina219_SetCalibration_16V_16A(void)
{
  u16 configValue;
  
  // By default we use a pretty huge range for the input voltage,
  // which probably isn't the most appropriate choice for system
  // that don't use a lot of power.  But all of the calculations
  // are shown below if you want to change the settings.  You will
  // also need to change any relevant register settings, such as
  // setting the VBUS_MAX to 16V instead of 32V, etc.
  
  // VBUS_MAX     = 16V   (Assumes 16V, can also be set to 32V)
  // VSHUNT_MAX   = 0.32  (Assumes Gain 8, 320mV, can also be 0.16, 0.08, 0.04)
  // RSHUNT       = 0.02   (Resistor value in ohms)
  
  // 1. Determine max possible current
  // MaxPossible_I = VSHUNT_MAX / RSHUNT
  // MaxPossible_I = 16A
  
  // 2. Determine max expected current
  // MaxExpected_I = 16A
  
  // 3. Calculate possible range of LSBs (Min = 15-bit, Max = 12-bit)
  // MinimumLSB = MaxExpected_I/32767
  // MinimumLSB = 0.00048            (0.48mA per bit)
  // MaximumLSB = MaxExpected_I/4096
  // MaximumLSB = 0,00390            (3.9mA per bit)
  
  // 4. Choose an LSB between the min and max values
  //    (Preferrably a roundish number close to MinLSB)
  // CurrentLSB = 0.00050            (500uA per bit)
  
  // 5. Compute the calibration register
  // Cal = trunc (0.04096 / (Current_LSB * RSHUNT))
  // Cal = 4096 (0x1000)
  
  // ina219_calValue = 0x1000;
  ina219_calValue = 0x0D90;  //0x1000;
  
  // 6. Calculate the power LSB
  // PowerLSB = 20 * CurrentLSB
  // PowerLSB = 0.01 (10mW per bit)
  
  // 7. Compute the maximum current and shunt voltage values before overflow
  //
  // Max_Current = Current_LSB * 32767
  // Max_Current = 16.3835A before overflow
  //
  // If Max_Current > Max_Possible_I then
  //    Max_Current_Before_Overflow = MaxPossible_I
  // Else
  //    Max_Current_Before_Overflow = Max_Current
  // End If
  //
  // Max_ShuntVoltage = Max_Current_Before_Overflow * RSHUNT
  // Max_ShuntVoltage = 0.32V
  //
  // If Max_ShuntVoltage >= VSHUNT_MAX
  //    Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX
  // Else
  //    Max_ShuntVoltage_Before_Overflow = Max_ShuntVoltage
  // End If
  
  // 8. Compute the Maximum Power
  // MaximumPower = Max_Current_Before_Overflow * VBUS_MAX
  // MaximumPower = 1.6 * 16V
  // MaximumPower = 256W
  
  // Set multipliers to convert raw current/power values
  ina219_current_LSB_uA = 100;     // Current LSB = 500uA per bit
  ina219_power_LSB_mW = 2;         // Power LSB = 10mW per bit = 20 * Current LSB
  
  // Set Calibration register to 'Cal' calculated above
  ina219_Write_Register(INA219_REG_CALIBRATION, ina219_calValue);
  
  // Set Config register to take into account the settings above
  configValue = ( INA219_CFG_BVOLT_RANGE_16V | INA219_CFG_SVOLT_RANGE_320MV | INA219_CFG_BADCRES_12BIT_16S_8MS | INA219_CFG_SADCRES_12BIT_16S_8MS | INA219_CFG_MODE_SANDBVOLT_CONTINUOUS );
  // configValue = ( INA219_CFG_BVOLT_RANGE_16V | INA219_CFG_SVOLT_RANGE_320MV | INA219_CFG_BADCRES_12BIT_32S_17MS | INA219_CFG_SADCRES_12BIT_32S_17MS | INA219_CFG_MODE_SANDBVOLT_CONTINUOUS );
  
  ina219_Write_Register(INA219_REG_CONFIG, configValue);
}
 
void ina219_configureRegisters(void)
{
  // ina219_Write_Register(INA219_REG_CONFIG, INA219_CFG_RESET); // 软件复位

  delay_ms(15);
  
  ina219_SetCalibration_16V_16A();
}
 
s16 ina219_GetBusVoltage_raw(void)
{
  s16 val;
  
  ina219_Read_Register(INA219_REG_BUSVOLTAGE, (u16*)&val);
  val >>= 3;                      // Shift to the right 3 to drop CNVR and OVF

  return (val);
}
 
s16 ina219_GetCurrent_raw(void)
{
  s16 val;
  
  // Sometimes a sharp load will reset the INA219, which will
  // reset the cal register, meaning CURRENT and POWER will
  // not be available ... avoid this by always setting a cal
  // value even if it's an unfortunate extra step
  ina219_Write_Register(INA219_REG_CALIBRATION, ina219_calValue);
  
  // Now we can safely read the CURRENT register!
  ina219_Read_Register(INA219_REG_CURRENT, (u16*)&val);
  
  return (val);
}
 
 
s16 ina219_GetBusVoltage_mV(void)
{
  s16 val;
  
  ina219_Read_Register(INA219_REG_BUSVOLTAGE, (u16*)&val);
  val >>= 3;                      // Shift to the right 3 to drop CNVR and OVF
  val *= ina219_busVolt_LSB_mV;   // multiply by LSB(4mV)
  
  return (s16)(val);
}
 
s32 ina219_GetShuntVoltage_uV(void)
{
  s32 val;
  s16 reg;
  
  ina219_Read_Register(INA219_REG_SHUNTVOLTAGE, (u16*)&reg);
  val = (s32)reg * ina219_shuntVolt_LSB_uV;   // multiply by LSB(10uV)
  
  return (s32)val;
}
 
s32 ina219_GetCurrent_uA(void)
{
  s32 val;
  s16 reg;

  // Sometimes a sharp load will reset the INA219, which will
  // reset the cal register, meaning CURRENT and POWER will
  // not be available ... avoid this by always setting a cal
  // value even if it's an unfortunate extra step
  ina219_Write_Register(INA219_REG_CALIBRATION, ina219_calValue);

  // Now we can safely read the CURRENT register!
  ina219_Read_Register(INA219_REG_CURRENT, (u16*)&reg);

  val = (s32)reg * ina219_current_LSB_uA;

  return (s32)(val);
}
 
s32 ina219_GetPower_mW(void)
{
  s32 val;
  s16 reg;

  // Sometimes a sharp load will reset the INA219, which will
  // reset the cal register, meaning CURRENT and POWER will
  // not be available ... avoid this by always setting a cal
  // value even if it's an unfortunate extra step
  ina219_Write_Register(INA219_REG_CALIBRATION, ina219_calValue);

  // Now we can safely read the POWER register!
  ina219_Read_Register(INA219_REG_POWER, (u16*)&reg);

  val = (s32)reg * ina219_power_LSB_mW;

  return (s32)(val);
}
 
void ina219_process(void)
{
 
  // TODO: 总线电压 Vin-和GND之间的电压
  ina219_value.voltage = ina219_GetBusVoltage_mV();
  log_d("ina219 voltage is:%d(mV)\r\n", ina219_value.voltage);

  // TODO: 分流电阻两端电压
  ina219_value.shunt = ina219_GetShuntVoltage_uV();
  log_d("ina219 shunt   is:%ld(uV)\r\n",ina219_value.shunt);
      
  // TODO: 电流
  ina219_value.current = ina219_GetCurrent_uA();
  log_d("ina219 current is:%ld(uA)\r\n",ina219_value.current);

  ina219_value.power = ina219_GetPower_mW();
  log_d("ina219 power   is:%ld(mW)\r\n",ina219_value.power);			

}


void drv_ina219_init(void)
{
    I2C_InitTypeDef i2c;
    i2c.I2C_Enable = ENABLE;
    i2c.I2C_Mode = I2C_Mode_Master;
    i2c.I2C_Speed = MAIN_Fosc/2/(100000*2+4);
    i2c.I2C_MS_WDTA = DISABLE;
    i2c.I2C_SL_MA = ENABLE;
    I2C_Init(&i2c);

    // NVIC_I2C_Init(I2C_Mode_Master, ENABLE, Priority_1);  // I2C Master模式可不用开中断

  #if 0
    P2_MODE_IO_PU(GPIO_Pin_4|GPIO_Pin_5);
    I2C_SW(I2C_P24_P25);
  #else
    P1_MODE_IO_PU(GPIO_Pin_4|GPIO_Pin_5);
    I2C_SW(I2C_P14_P15);
  #endif

    ina219_configureRegisters();

    log_d("INA219 Driver Init\n");
}

#endif
